package com.example.spider.proxy;

/**
 * 计算最近请求表现 todo 不同业务打分标准不同，如爬取小的主要看响应时间，爬取大的主要看带宽、请求总数、成功率。即：单位时间内有效下次量（条数/字节）
 * <p>
 * 默认 history 为 byte[30+]，相当于只看这个代理的最近测试表现（被测网站必须高可用，低延迟，无限流 todo 监测是否达标 TPS）
 * byte[x] historyPerform 中的值为最近x次请求耗时，负数为失败（最大为-1），正数为成功（至少为1），0为未请求
 * 其中数字为响应时间 ms / 100，也就是最大 12_700 对应12.7s，响应时间更大时认为 12.7s 得到响应
 */
public class RecentlyRequestScoreCalculator {

    /**
     * 计算单次请求耗时 byte值
     *
     * @param success  是否成功（1xx2xx 成功，3xx4xx不算，5xx失败）
     * @param costTime 请求耗时
     * @return 将请求表现映射为 [-127, -1]  [1, 127]
     */
    public static byte responseScore(boolean success, long costTime) {
        int base = success ? 1 : -1;
        int score = (int) (costTime / 100);
        if (score > 127) {
            score = 127;
        }
        score *= base;
        return (byte) (score == 0 ? score + base : score);
    }

    /**
     * 计算得分-更精准
     * 因素：历史响应时间、响应时间方差、平均响应耗时、整体成功率、连续成功/失败数目
     */
    public static int calculateScore(byte[] historyPerform) {
        assert historyPerform != null;
        // 请求总数
        int totalCount = 0;
        // 成功总数
        int successCount = 0;
        // 成功请求总时间
        int successTotalCostTime = 0;

        // 平均耗时
        // int averageCost = 0;
        // 最大连续成功数
        //int maxSuccessContinuityCount = 0;
        // 最大连续失败数目
        //int minFailContinuityCount;

        for (int i = 0; i < historyPerform.length; i++) {
            int requestScore = historyPerform[i];
            if (requestScore == 0) {
                totalCount = i;
                break;
            }
            boolean success = requestScore > 0;
            if (success) {
                successCount++;
                successTotalCostTime += requestScore;
            }
        }
        if (totalCount < 3) {
            // 默认分
            return 10;
        }
        // 成功率
        int successPercentage = successCount * 100 / totalCount;
        // 平均成功耗时
        int successAverageCost = successTotalCostTime / totalCount;
        // 耗时方差


        // 最终得分 根据自己需求来制定，或者直接返回这个值，为代理打标
        int score = 0;

        return score;
    }

    /**
     * 将历史请求记录转为记分权重 array（）
     * @param historyPerform 历史请求记录，循环记录。
     * @return 记分数组：顺序：最近的...最久一次请求
     */
    public static byte[] transferToScoreWeightArray(byte[] historyPerform, int currentIndex) {
        assert historyPerform != null && historyPerform.length > 10;
        // 最近 10 次表现比重更大，一次顶2次看，其余作为历史参考
        byte[] totalPerform = new byte[historyPerform.length + 10];
        int index = currentIndex;
        // 填充结果前20位
        for (int i = 0; i < 20; i = i + 2) {
            byte score = historyPerform[index];
            if (score == 0) {
                // 本次无分数，不合法
                return totalPerform;
            }
            totalPerform[i] = score;
            totalPerform[i + 1] = score;
            // 挪到上次请求计分下标（往前遍历）
            index = index > 0 ? index - 1 : historyPerform.length;
        }
        // 从第二十位到结尾
        for (int i = 20; i < totalPerform.length; i++) {
            // 挪到上次请求计分下标
            //index = index > 0 ? index - 1 : historyPerform.length;
            byte score = historyPerform[index];
            if (score == 0) {
                return totalPerform;
            }
            totalPerform[i + 1] = score;
            // 挪到上次请求计分下标
            index = index > 0 ? index - 1 : historyPerform.length;
        }
        return totalPerform;
    }

}
